home *** CD-ROM | disk | FTP | other *** search
/ Light ROM 1 / LIGHT-ROM 1 (Amiga Library Services)(1994).iso / ffdisks / d980.lha / APipe / pgmpipe.c < prev    next >
C/C++ Source or Header  |  1994-04-04  |  25KB  |  860 lines

  1. /*
  2. **  Pgmpipe.c --- main module of APipe-Handler
  3. **
  4. **  Copyright (C) 1991 by Per Bojsen.  All Rights Reserved.
  5. **
  6. **  Permission is granted to any individual or institution to use, copy,
  7. **  modify, and distribute this software, provided that this complete
  8. **  copyright and permission notice is maintained, intact, in all copies
  9. **  and supporting documentation.
  10. **
  11. **  This software is provided on an "as is" basis without express or
  12. **  implied warranty.
  13. **
  14. **  NOTE: The code is reentrant.  Be careful when modifying it.
  15. **
  16. **  $Id: pgmpipe.c,v 1.7 92/09/12 16:49:11 bojsen Exp Locker: bojsen $
  17. **
  18. **  $Log:    pgmpipe.c,v $
  19. **  Revision 1.7  92/09/12  16:49:11  bojsen
  20. **  Improved cloning of file handles so that reading/writing will start
  21. **  from current position.
  22. **  
  23. **  Revision 1.6  92/02/20  23:46:11  bojsen
  24. **  Fixed bug in handling multiple outstanding read packets add EOF.
  25. **
  26. **  Revision 1.5  91/11/24  17:01:25  bojsen
  27. **  Made extraction of command line from file name more robust; the code now
  28. **  handles the case where the handler name is not prepended.
  29. **
  30. **  Revision 1.4  91/11/24  02:22:39  bojsen
  31. **  First released version.  Changed references to ChildProcess() to
  32. **  _ChildProcess().
  33. **
  34. **  Revision 1.3  91/10/01  02:35:30  bojsen
  35. **  Added code to clone current dir of requester process.  Changed code
  36. **  to obtain pointer to requester process.
  37. **
  38. **  Revision 1.2  91/09/24  23:59:44  bojsen
  39. **  Fixed typo in #include line.
  40. **
  41. **  Revision 1.1  91/09/24  23:54:06  bojsen
  42. **  Initial revision
  43. **
  44. */
  45.  
  46. #include <exec/types.h>
  47. #include <exec/memory.h>
  48. #include <utility/tagitem.h>
  49. #include <clib/macros.h>
  50. #include <dos/dos.h>
  51. #include <dos/dosextens.h>
  52. #include <dos/dostags.h>
  53. #include <dos/var.h>
  54. #ifdef __SASC
  55. #include <proto/exec.h>
  56. #include <proto/dos.h>
  57. #endif /* __SASC */
  58. #include <string.h>
  59. #include "APipe-Handler_rev.h"
  60.  
  61. /*
  62.  *  Version tagging.
  63.  */
  64. STATIC char VersionTag[] = VERSTAG;
  65.  
  66. /*
  67.  *  Debugging.
  68.  */
  69. #ifdef DEBUG
  70. #define DPrintF(args) do { KPrintF args; } while (0)
  71. #else /* !DEBUG */
  72. #define DPrintF(args)
  73. #endif /* !DEBUG */
  74.  
  75. #define AbsExecBase 4L
  76. #define LIB_VERSION_SUPPORTED 37L
  77. #define PIPEBUFFER_SIZE 4096
  78.  
  79. /* defines for read/write status */
  80. #define PPIPE_NOTOPEN   0
  81. #define PPIPE_OPEN      4
  82. #define PPIPE_READ      1
  83. #define PPIPE_READOPEN  (PGMPIPE_READ | PGMPIPE_OPEN)
  84. #define PPIPE_WRITE     2
  85. #define PPIPE_WRITEOPEN (PGMPIPE_WRITE | PGMPIPE_OPEN)
  86. #define PPIPE_RWMASK    3
  87.  
  88. /* defines for fh_Arg1 field of filehandles */
  89. #define PPIPE_READER  1
  90. #define PPIPE_WRITER  2
  91.  
  92. /* defines for closing variable */
  93. #define PPIPE_C_OPEN          0
  94. #define PPIPE_C_RDRCLOSING    1
  95. #define PPIPE_C_WRTCLOSING    2
  96. #define PPIPE_C_CLOSINGMASK   3
  97. #define PPIPE_C_RDRCLOSED     4
  98. #define PPIPE_C_WRTCLOSED     8
  99. #define PPIPE_C_CLOSEDMASK  0xC
  100.  
  101. /*
  102.  *  Parameter packet to child process.
  103.  */
  104. struct ChildMsg
  105. {
  106.   struct Message  ExecMsg;
  107.   char           *CmdLine;   /* the command line to execute */
  108.   LONG            StackSize; /* use this stacksize for child's child */
  109.   BPTR            PathList;  /* path list for child */
  110.   ULONG           Flags;     /* flags for child */
  111.   LONG            RC;        /* return code from child process */
  112. };
  113.  
  114. /* Defines for ChildMsg.Flags */
  115. #define CHMF_PIPE 1L /* stdin of child is a pipe (of PIPE: type) */
  116. #define CHMF_PATH 2L /* path is passed in in PathList */
  117.  
  118. /*
  119.  *  Command path element.
  120.  */
  121. struct PathEntry
  122. {
  123.   BPTR pe_NextPathEntry;
  124.   BPTR pe_PathLock;
  125. };
  126.  
  127. /*
  128.  *  The library bases must be global by AmigaOS programming standards,
  129.  *  and the pragma libcall requires them.
  130.  */
  131. struct ExecBase *SysBase;
  132. struct DosLibrary *DOSBase;
  133.  
  134.  
  135. /*
  136.  *  Prototypes for external functions.
  137.  */
  138. extern int _ChildProcess(void);
  139.  
  140. /*
  141.  *  Prototypes for local functions.
  142.  */
  143. ULONG CopyFromFIFO(UBYTE *FIFO, ULONG FIFOSize, UBYTE **Start, UBYTE *End,
  144.                    ULONG *FIFOFill, UBYTE *Dest, ULONG NumBytes);
  145. ULONG CopyToFIFO(UBYTE *FIFO, ULONG FIFOSize, UBYTE **Start, UBYTE **End,
  146.                  ULONG *FIFOFill, UBYTE *Src, ULONG NumBytes);
  147. STRPTR GetCommandLine(STRPTR FileSpec, BSTR HandlerName);
  148. LONG ExecCommand(char *CmdLine, struct MsgPort **ParentPort,
  149.                  struct Process *Requester, struct FileHandle *ChildIO,
  150.                  LONG IoDirection);
  151. BPTR CloneProcessIO(struct Process *Friend, LONG IoDirection);
  152. BPTR ClonePathList(struct CommandLineInterface *Peer);
  153. void FreePathList(BPTR);
  154.  
  155. void
  156. PgmPipe()
  157. {
  158.   struct DosLibrary *l_DOSBase;
  159.  
  160.   SysBase = *((struct ExecBase **) AbsExecBase); /* `open' exec.library */
  161.  
  162.   if (l_DOSBase =
  163.         (struct DosLibrary *) OpenLibrary("dos.library", LIB_VERSION_SUPPORTED))
  164.   {
  165.     struct Process *ourProc, *theirProc;
  166.     struct DosList *ourDevNode;
  167.     struct DosPacket *packet, *writePkt = NULL, *readPkt = NULL;
  168.     struct DosPacket *writeCls = NULL, *readCls = NULL;
  169.     struct MsgPort *childPort = NULL;
  170.     struct MsgPort *oldConTask = NULL;
  171.     struct ChildMsg *childMsg = NULL;
  172.     struct FileHandle *fhIncoming, *fhOutgoing;
  173.     UBYTE *pipeBuffer;
  174.     UBYTE *pbStart, *pbEnd;
  175.     ULONG bytesReady, bytesR = 0, bytesW = 0;
  176.     UWORD closing = PPIPE_C_RDRCLOSED | PPIPE_C_WRTCLOSED;
  177.     UWORD readwrite = PPIPE_NOTOPEN;
  178.     ULONG waitSigMask;
  179.     struct MinList readQ, writeQ;
  180. #ifdef DEBUG
  181.     ULONG packetNum = 0;
  182. #endif /* DEBUG */
  183.  
  184.     NewList((struct List *) &readQ);
  185.     NewList((struct List *) &writeQ);
  186.  
  187.     DOSBase = l_DOSBase; /* patch the global library base */
  188.  
  189.     ourProc = (struct Process *) FindTask(NULL);
  190.     waitSigMask = 1L << ourProc->pr_MsgPort.mp_SigBit;
  191.     packet = WaitPkt(); /* get parameter packet */
  192.  
  193.     if ((pipeBuffer = AllocMem(PIPEBUFFER_SIZE, 0)) == NULL)
  194.       ReplyPkt(packet, DOSFALSE, ERROR_NO_FREE_STORE);
  195.     else
  196.     {
  197.       pbStart = pbEnd = pipeBuffer;
  198.       bytesReady = 0;
  199.  
  200.       /*
  201.        *  Currently we don't use the parameters provided in the parameter
  202.        *  packet.  Since we want a new process for each instance of reference
  203.        *  to our handler we don't patch the DeviceList node, but use the
  204.        *  node to get the handler name.
  205.        */
  206.  
  207.       ourDevNode = (struct DosList *) BADDR(packet->dp_Arg3);
  208.  
  209.       ReplyPkt(packet, DOSTRUE, packet->dp_Arg2);
  210.  
  211.       do
  212.       {
  213.         struct Node *node;
  214.         char *cmdLine;
  215.         LONG ioerr;
  216.  
  217.         if (readPkt && bytesReady == 0 && writePkt == NULL &&
  218.             closing & PPIPE_C_WRTCLOSED)
  219.         {
  220.           ReplyPkt(readPkt, bytesR, 0);
  221.           while (readPkt = (struct DosPacket *)
  222.                              (node = RemHead((struct List *) &readQ),
  223.                               node ? node->ln_Name : NULL))
  224.             ReplyPkt(readPkt, 0, 0);
  225.         }
  226.  
  227. #ifdef DEBUG
  228.         if (readPkt || writePkt)
  229.           DPrintF(("Event loop: readPkt == 0x%08lx writePkt == 0x%08lx\n",
  230.                    readPkt, writePkt));
  231. #endif /* DEBUG */
  232.  
  233.         if (packet = WaitPkt())
  234.         {
  235.           DPrintF(("Packet %ld, port 0x%08lx: ", ++packetNum, packet->dp_Port));
  236.  
  237.           switch (packet->dp_Type)
  238.           {
  239.             struct TagItem adoTags[2];
  240.  
  241.             case ACTION_FINDINPUT:
  242.               DPrintF(("ACTION_FINDINPUT 0x%08lx 0x%08lx %s\n",
  243.                        packet->dp_Arg1 << 2, packet->dp_Arg2 << 2,
  244.                        (char *) (packet->dp_Arg3 << 2) + 1));
  245.  
  246.               if (readwrite != PPIPE_NOTOPEN)
  247.               {
  248.                 ReplyPkt(packet, DOSFALSE, ERROR_OBJECT_IN_USE);
  249.                 break;
  250.               }
  251.               else
  252.                 readwrite = PPIPE_READ;
  253.  
  254.             case ACTION_FINDOUTPUT:
  255. #ifdef DEBUG
  256.               if (packet->dp_Type == ACTION_FINDOUTPUT)
  257.                 DPrintF(("ACTION_FINDOUTPUT 0x%08lx 0x%08lx %s\n",
  258.                          packet->dp_Arg1 << 2, packet->dp_Arg2 << 2,
  259.                          (char *) (packet->dp_Arg3 << 2) + 1));
  260. #endif /* DEBUG */
  261.  
  262.               if (readwrite & PPIPE_OPEN)
  263.               {
  264.                 ReplyPkt(packet, DOSFALSE, ERROR_OBJECT_IN_USE);
  265.                 break;
  266.               }
  267.               else if (readwrite != PPIPE_READ)
  268.                 readwrite = PPIPE_WRITE;
  269.  
  270.               fhIncoming = (struct FileHandle *) BADDR(packet->dp_Arg1);
  271.  
  272.               /*
  273.                *  Obtain command line to execute.
  274.                */
  275.               cmdLine = GetCommandLine((STRPTR) BADDR(packet->dp_Arg3) + 1,
  276.                                        ourDevNode->dol_Name);
  277.  
  278.               /*
  279.                *  Find out who sent the packet.
  280.                */
  281.               if ((packet->dp_Port->mp_Flags & PF_ACTION) == PA_SIGNAL)
  282.                 theirProc = (struct Process *) packet->dp_Port->mp_SigTask;
  283.               else
  284.               {
  285.                 ReplyPkt(packet, DOSFALSE, ERROR_BAD_STREAM_NAME);
  286.                 readwrite = PPIPE_NOTOPEN;
  287.                 break;
  288.               }
  289.  
  290.               adoTags[0].ti_Tag = ADO_FH_Mode;
  291.               adoTags[0].ti_Data =
  292.                 readwrite == PPIPE_READ ? MODE_NEWFILE : MODE_OLDFILE;
  293.               adoTags[1].ti_Tag = TAG_DONE;
  294.  
  295.               if ((fhOutgoing = AllocDosObject(DOS_FILEHANDLE, adoTags))
  296.                     == NULL)
  297.               {
  298.                 ReplyPkt(packet, DOSFALSE, ERROR_NO_FREE_STORE);
  299.                 readwrite = PPIPE_NOTOPEN;
  300.                 break;
  301.               }
  302.  
  303.               fhOutgoing->fh_Link = NULL;
  304.               fhOutgoing->fh_Port = fhIncoming->fh_Port = (struct MsgPort *) 0;
  305.               fhOutgoing->fh_Type = &ourProc->pr_MsgPort;
  306.  
  307.               if (readwrite == PPIPE_READ)
  308.               {
  309.                 fhIncoming->fh_Arg1 = PPIPE_READER;
  310.                 fhOutgoing->fh_Arg1 = PPIPE_WRITER;
  311.               }
  312.               else
  313.               {
  314.                 fhIncoming->fh_Arg1 = PPIPE_WRITER;
  315.                 fhOutgoing->fh_Arg1 = PPIPE_READER;
  316.               }
  317.  
  318.               oldConTask = SetConsoleTask(theirProc->pr_ConsoleTask);
  319.  
  320.               DPrintF(("Getting ready to spawn child . . .  "));
  321.  
  322.               if (ioerr = ExecCommand(cmdLine, &childPort, theirProc,
  323.                                       fhOutgoing, readwrite))
  324.               {
  325.                 SetConsoleTask(oldConTask);
  326.                 ReplyPkt(packet, DOSFALSE, ioerr);
  327.                 FreeDosObject(DOS_FILEHANDLE, fhOutgoing);
  328.                 readwrite = PPIPE_NOTOPEN;
  329.                 break;
  330.               }
  331.               SetConsoleTask(oldConTask);
  332.  
  333.               DPrintF(("Child spawned.\n"));
  334.  
  335.               waitSigMask |= 1L << childPort->mp_SigBit;
  336.  
  337.               closing = PPIPE_C_OPEN;
  338.               readwrite |= PPIPE_OPEN;
  339.  
  340.               ReplyPkt(packet, DOSTRUE, packet->dp_Res2);
  341.  
  342.               break;
  343.  
  344.             case ACTION_READ:
  345.               DPrintF(("ACTION_READ %ld 0x%08lx %ld\n", packet->dp_Arg1,
  346.                        packet->dp_Arg2, packet->dp_Arg3));
  347.  
  348.               if (packet->dp_Arg2 == NULL || packet->dp_Arg3 < 0)
  349.                 ReplyPkt(packet, DOSFALSE, ERROR_BAD_NUMBER);
  350.               else if (closing & PPIPE_C_RDRCLOSED)
  351.                 ReplyPkt(packet, DOSFALSE, ERROR_INVALID_LOCK);
  352.               else if (packet->dp_Arg1 == PPIPE_WRITER)
  353.                 ReplyPkt(packet, 0, 0);
  354.               else if (readPkt == NULL)
  355.               {
  356.                 readPkt = packet;
  357.                 bytesR = 0;
  358.               }
  359.               else
  360.                 AddTail((struct List *) &readQ,
  361.                         (struct Node *) packet->dp_Link);
  362.               break;
  363.  
  364.             case ACTION_WRITE:
  365.               DPrintF(("ACTION_WRITE %ld 0x%08lx %ld\n", packet->dp_Arg1,
  366.                        packet->dp_Arg2, packet->dp_Arg3));
  367.  
  368.               if (packet->dp_Arg2 == NULL || packet->dp_Arg3 < 0)
  369.                 ReplyPkt(packet, DOSFALSE, ERROR_BAD_NUMBER);
  370.               else if (closing & PPIPE_C_WRTCLOSED)
  371.                 ReplyPkt(packet, DOSFALSE, ERROR_INVALID_LOCK);
  372.               else if (packet->dp_Arg1 == PPIPE_READER)
  373.                 ReplyPkt(packet, 0, 0);
  374.               else if (writePkt == NULL)
  375.               {
  376.                 writePkt = packet;
  377.                 bytesW = 0;
  378.               }
  379.               else
  380.                 AddTail((struct List *) &writeQ,
  381.                         (struct Node *) packet->dp_Link);
  382.               break;
  383.  
  384.             case ACTION_END:
  385.               DPrintF(("ACTION_END %ld\n", packet->dp_Arg1));
  386.  
  387.               if (packet->dp_Arg1 == PPIPE_READER &&
  388.                   (closing & PPIPE_C_RDRCLOSED) == 0)
  389.               {
  390.                 closing |= PPIPE_C_RDRCLOSING;
  391.                 readCls = packet;
  392.               }
  393.               else if (packet->dp_Arg1 == PPIPE_WRITER &&
  394.                        (closing & PPIPE_C_WRTCLOSED) == 0)
  395.               {
  396.                 closing |= PPIPE_C_WRTCLOSING;
  397.                 writeCls = packet;
  398.               }
  399.               else
  400.                 ReplyPkt(packet, DOSTRUE, 0);
  401.               break;
  402.  
  403.             case ACTION_IS_FILESYSTEM:
  404.               DPrintF(("ACTION_IS_FILESYSTEM\n"));
  405.  
  406.               ReplyPkt(packet, DOSFALSE, 0);
  407.               break;
  408.  
  409.             default:
  410.               DPrintF(("%ld\n", packet->dp_Type));
  411.  
  412.               ReplyPkt(packet, DOSFALSE, ERROR_ACTION_NOT_KNOWN);
  413.           }
  414.  
  415.           DPrintF(("\n"));
  416.         }
  417.  
  418.         do
  419.         {
  420.           if (readPkt)
  421.           {
  422.             if (bytesReady > 0)
  423.               bytesR += CopyFromFIFO(pipeBuffer, PIPEBUFFER_SIZE, &pbStart,
  424.                                      pbEnd, &bytesReady,
  425.                                      (UBYTE *) readPkt->dp_Arg2 + bytesR,
  426.                                      readPkt->dp_Arg3 - bytesR);
  427.             if (bytesR < readPkt->dp_Arg3 && writePkt)
  428.             {
  429.               ULONG bytes = MIN(writePkt->dp_Arg3 - bytesW,
  430.                                 readPkt->dp_Arg3 - bytesR);
  431.  
  432.               CopyMem((UBYTE *) writePkt->dp_Arg2 + bytesW,
  433.                       (UBYTE *) readPkt->dp_Arg2 + bytesR, bytes);
  434.               bytesR += bytes;
  435.               if ((bytesW += bytes) == writePkt->dp_Arg3)
  436.               {
  437.                 ReplyPkt(writePkt, writePkt->dp_Arg3, 0);
  438.                 writePkt = NULL;
  439.               }
  440.             }
  441.             if (bytesR == readPkt->dp_Arg3)
  442.             {
  443.               ReplyPkt(readPkt, readPkt->dp_Arg3, 0);
  444.               readPkt = NULL;
  445.             }
  446.           }
  447.           if (readPkt == NULL &&
  448.               (readPkt = (struct DosPacket *)
  449.                            (node = RemHead((struct List *) &readQ),
  450.                             node ? node->ln_Name : NULL)))
  451.           {
  452.             bytesR = 0;
  453.           }
  454.           if (writePkt == NULL &&
  455.               (writePkt = (struct DosPacket *)
  456.                             (node = RemHead((struct List *) &writeQ),
  457.                              node ? node->ln_Name : NULL)))
  458.           {
  459.             bytesW = 0;
  460.           }
  461.         }
  462.         while (readPkt && (writePkt || bytesReady > 0));
  463.  
  464.         if (readPkt == NULL && closing & PPIPE_C_RDRCLOSING)
  465.         {
  466.           closing &= ~PPIPE_C_RDRCLOSING;
  467.           closing |= PPIPE_C_RDRCLOSED;
  468.         }
  469.  
  470.         while (writePkt &&
  471.                (closing & PPIPE_C_RDRCLOSED ||
  472.                 PIPEBUFFER_SIZE - bytesReady >= writePkt->dp_Arg3 - bytesW))
  473.         {
  474.           if (closing & PPIPE_C_RDRCLOSED)
  475.           {
  476.             ReplyPkt(writePkt, writePkt->dp_Arg3, 0);
  477.             writePkt = NULL;
  478.           }
  479.           else
  480.           {
  481.             ULONG bytes = CopyToFIFO(pipeBuffer, PIPEBUFFER_SIZE, &pbStart,
  482.                                      &pbEnd, &bytesReady,
  483.                                      (UBYTE *) writePkt->dp_Arg2 + bytesW,
  484.                                      writePkt->dp_Arg3 - bytesW);
  485.  
  486.             if ((bytesW += bytes) == writePkt->dp_Arg3)
  487.             {
  488.               ReplyPkt(writePkt, writePkt->dp_Arg3, 0);
  489.               writePkt = NULL;
  490.             }
  491.           }
  492.           if (writePkt == NULL &&
  493.               (writePkt = (struct DosPacket *)
  494.                             (node = RemHead((struct List *) &writeQ),
  495.                              node ? node->ln_Name : NULL)))
  496.           {
  497.             bytesW = 0;
  498.           }
  499.         }
  500.  
  501.         if (writePkt == NULL && closing & PPIPE_C_WRTCLOSING)
  502.         {
  503.           closing &= ~PPIPE_C_WRTCLOSING;
  504.           closing |= PPIPE_C_WRTCLOSED;
  505.         }
  506.       }
  507.       while ((closing & PPIPE_C_RDRCLOSED) == 0 ||
  508.              (closing & PPIPE_C_WRTCLOSED) == 0);
  509.  
  510.       /* clean up from child process */
  511.       if (childPort)
  512.       {
  513.         waitSigMask = 1L << childPort->mp_SigBit;
  514.  
  515.         while ((childMsg = (struct ChildMsg *) GetMsg(childPort)) == NULL)
  516.           Wait(waitSigMask);
  517.  
  518.         DeleteMsgPort(childPort);
  519.       }
  520.       if (childMsg) /* means the child process existed */
  521.       {
  522.         if (readCls)
  523.           ReplyPkt(readCls, DOSTRUE, 0);
  524.         if (writeCls)
  525.           ReplyPkt(writeCls, DOSTRUE, 0);
  526.         FreePathList(childMsg->PathList);
  527.         FreeVec(childMsg); /* no use for return code currently */
  528.       }
  529.       FreeMem(pipeBuffer, PIPEBUFFER_SIZE);
  530.     }
  531.  
  532.     CloseLibrary((struct Library *) l_DOSBase);
  533.  
  534.     DPrintF(("Handler exiting.\n"));
  535.   }
  536. } /* PgmPipe */
  537.  
  538.  
  539. STATIC ULONG
  540. CopyFromFIFO(UBYTE *FIFO, ULONG FIFOSize, UBYTE **Start, UBYTE *End,
  541.              ULONG *FIFOFill, UBYTE *Dest, ULONG NumBytes)
  542. {
  543.   ULONG bytesCopied = 0, bytes;
  544.  
  545.   DPrintF(("CopyFromFIFO: 0x%08lx %ld", Dest, NumBytes));
  546.  
  547.   if (*FIFOFill > 0 && NumBytes > 0)
  548.   {
  549.     bytes = *Start >= End ? FIFO + FIFOSize - *Start : *FIFOFill;
  550.  
  551.     CopyMem(*Start, Dest, bytesCopied = MIN(bytes, NumBytes));
  552.     NumBytes -= bytesCopied;
  553.     *Start += bytesCopied;
  554.     *FIFOFill -= bytesCopied;
  555.   }
  556.   if (*FIFOFill > 0 && NumBytes > 0)
  557.   {
  558.     bytes = End - FIFO; /* invariant: *Start == FIFO + FIFOSize */
  559.  
  560.     CopyMem(FIFO, Dest + bytesCopied, bytes = MIN(bytes, NumBytes));
  561.     *Start = FIFO + bytes;
  562.     *FIFOFill -= bytes;
  563.     bytesCopied += bytes;
  564.   }
  565.  
  566.   DPrintF((" %ld\n\n", bytesCopied));
  567.  
  568.   return bytesCopied;
  569. } /* CopyFromFIFO */
  570.  
  571.  
  572. STATIC ULONG
  573. CopyToFIFO(UBYTE *FIFO, ULONG FIFOSize, UBYTE **Start, UBYTE **End,
  574.            ULONG *FIFOFill, UBYTE *Src, ULONG NumBytes)
  575. {
  576.   ULONG bytesCopied = 0, bytes;
  577.  
  578.   DPrintF(("CopyToFIFO: 0x%08lx %ld", Src, NumBytes));
  579.  
  580.   if (*FIFOFill == 0)
  581.     *Start = *End = FIFO; /* normalize for efficiency */
  582.  
  583.   if (*FIFOFill < FIFOSize && NumBytes > 0)
  584.   {
  585.     bytes = *End <= *Start ? FIFOSize - *FIFOFill : FIFO + FIFOSize - *End;
  586.  
  587.     CopyMem(Src, *End, bytesCopied = MIN(bytes, NumBytes));
  588.     NumBytes -= bytesCopied;
  589.     *End += bytesCopied;
  590.     *FIFOFill += bytesCopied;
  591.   }
  592.   if (*FIFOFill < FIFOSize && NumBytes > 0)
  593.   {
  594.     bytes = *Start - FIFO; /* invariant: *End == FIFO + FIFOSize */
  595.  
  596.     CopyMem(Src + bytesCopied, FIFO, bytes = MIN(bytes, NumBytes));
  597.     *End = FIFO + bytes;
  598.     *FIFOFill += bytes;
  599.     bytesCopied += bytes;
  600.   }
  601.  
  602.   DPrintF((" %ld\n\n", bytesCopied));
  603.  
  604.   return bytesCopied;
  605. } /* CopyFromFIFO */
  606.  
  607.  
  608. #define GetCICh(String, Char) \
  609.   ((Char) = *(String), (Char) += (Char) >= 'A' && (Char) <= 'Z' ? 0x20 : 0)
  610.  
  611. STATIC STRPTR
  612. GetCommandLine(STRPTR FileSpec, BSTR HandlerName)
  613. {
  614.   UBYTE *cp1 = FileSpec, *cp2 = BADDR(HandlerName);
  615.   LONG hNmLen = *cp2++;
  616.   LONG ch1, ch2;
  617.  
  618.   while (hNmLen-- > 0 && GetCICh(cp1, ch1) == GetCICh(cp2, ch2))
  619.     cp1++, cp2++;
  620.  
  621.   return hNmLen < 0 && *cp1 == ':' ? cp1 + 1 : FileSpec;
  622. } /* GetCommandLine */
  623.  
  624.  
  625. #define FAILATCMD        "FailAt 2147483647\n"
  626. #define STRLEN_FAILATCMD 18
  627.  
  628. /*
  629.  *  Run a command asynchronously.
  630.  */
  631.  
  632. STATIC LONG
  633. ExecCommand(char *CmdLine, struct MsgPort **ParentPort,
  634.             struct Process *Requester, struct FileHandle *ChildIO,
  635.             LONG IoDirection)
  636. {
  637.   char *commandLine;
  638.   struct ChildMsg *childMsg;
  639.   struct TagItem processTags[9];
  640.   struct Process *child, *thisProcess = (struct Process *) FindTask(NULL);
  641.   struct CommandLineInterface *cli = BADDR(Requester->pr_CLI);
  642.   BPTR childOI;
  643.   struct LocalVar *pVar;
  644.   BPTR pathList, homeDir;
  645.   LONG ioErr;
  646.  
  647.   /*
  648.    *  Copy shell variables, aliases, and path from requester process.
  649.    *  NOTE: This is not completely safe!
  650.    */
  651.   Forbid();
  652.   for (pVar = (struct LocalVar *) Requester->pr_LocalVars.mlh_TailPred;
  653.        pVar->lv_Node.ln_Pred;
  654.        pVar = (struct LocalVar *) pVar->lv_Node.ln_Pred)
  655.   {
  656.     if (FindVar(pVar->lv_Node.ln_Name, pVar->lv_Node.ln_Type) == NULL)
  657.       SetVar(pVar->lv_Node.ln_Name, pVar->lv_Value, pVar->lv_Len,
  658.              pVar->lv_Flags | pVar->lv_Node.ln_Type & ~LVF_IGNORE);
  659.   }
  660.  
  661.   pathList = ClonePathList(cli);
  662.  
  663.   Permit();
  664.  
  665.   if ((childOI = CloneProcessIO(Requester, IoDirection)) == NULL)
  666.   {
  667.     ioErr = IoErr();
  668.     FreePathList(pathList);
  669.  
  670.     return ioErr;
  671.   }
  672.  
  673.   if ((homeDir = DupLock(Requester->pr_CurrentDir)) == NULL)
  674.   {
  675.     ioErr = IoErr();
  676.     Close(childOI);
  677.     FreePathList(pathList);
  678.  
  679.     return ioErr;
  680.   }
  681.  
  682.   /*
  683.    *  Create parent's port.
  684.    */
  685.   if ((*ParentPort = CreateMsgPort()) == NULL)
  686.   {
  687.     UnLock(homeDir);
  688.     Close(childOI);
  689.     FreePathList(pathList);
  690.  
  691.     return ERROR_NO_FREE_STORE;
  692.   }
  693.  
  694.   /*
  695.    *  Build command line from argument vector.  First count the length.
  696.    *
  697.    *  We insert a `FailAt 2147483647' (0x7fffffff) so the shell doesn't
  698.    *  print `Command foo failed' when we call System() to execute the
  699.    *  command.
  700.    */
  701.   if ((childMsg = AllocVec(sizeof(struct ChildMsg) + strlen(CmdLine) +
  702.                            STRLEN_FAILATCMD + 1, MEMF_PUBLIC)) == NULL)
  703.   {
  704.     DeleteMsgPort(*ParentPort);
  705.     *ParentPort = NULL;
  706.     UnLock(homeDir);
  707.     Close(childOI);
  708.     FreePathList(pathList);
  709.  
  710.     return ERROR_NO_FREE_STORE;
  711.   }
  712.  
  713.   childMsg->ExecMsg.mn_Node.ln_Type = NT_MESSAGE;
  714.   childMsg->ExecMsg.mn_Node.ln_Pri = 0;
  715.   childMsg->ExecMsg.mn_ReplyPort = *ParentPort;
  716.   childMsg->ExecMsg.mn_Length = sizeof(struct ChildMsg);
  717.   childMsg->CmdLine = commandLine = (char *) (childMsg + 1);
  718.  
  719.   /* Setup the grandchild's [sic] stack */
  720.   childMsg->StackSize = cli ? cli->cli_DefaultStack << 2 :
  721.                               Requester->pr_StackSize;
  722.   childMsg->PathList = pathList;
  723.   childMsg->Flags = pathList ? CHMF_PATH : 0;
  724.   childMsg->RC = 0;
  725.  
  726.   strcpy(commandLine, FAILATCMD);
  727.   strcpy(commandLine + STRLEN_FAILATCMD, CmdLine);
  728.  
  729.   processTags[0].ti_Tag = NP_Entry;
  730.   processTags[0].ti_Data = (Tag) _ChildProcess;
  731.   processTags[1].ti_Tag = NP_Input;
  732.   processTags[2].ti_Tag = NP_Output;
  733.   processTags[3].ti_Tag = NP_StackSize;
  734.   processTags[3].ti_Data = thisProcess->pr_StackSize;
  735.   processTags[4].ti_Tag = NP_Cli;
  736.   processTags[4].ti_Data = TRUE;
  737.   processTags[5].ti_Tag = NP_Name;
  738.   processTags[5].ti_Data = (Tag) "Kicker Process";
  739.   processTags[6].ti_Tag = NP_Priority;
  740.   processTags[6].ti_Data = Requester->pr_Task.tc_Node.ln_Pri;
  741.   processTags[7].ti_Tag = NP_CurrentDir;
  742.   processTags[7].ti_Data = homeDir;
  743.   processTags[8].ti_Tag = TAG_DONE;
  744.  
  745.   if (IoDirection == PPIPE_READ)
  746.   {
  747.     processTags[1].ti_Data = childOI;
  748.     processTags[2].ti_Data = MKBADDR(ChildIO);
  749.   }
  750.   else /* IoDirection == PPIPE_WRITE */
  751.   {
  752.     processTags[1].ti_Data = MKBADDR(ChildIO);
  753.     processTags[2].ti_Data = childOI;
  754.   }
  755.  
  756.   /*
  757.    *  Start our `kicker' process.  This process consists of the _ChildProcess()
  758.    *  function above.  The _ChildProcess() function then executes the command.
  759.    */
  760.   if ((child = CreateNewProc(processTags)) == NULL)
  761.   {
  762.     FreeVec(childMsg);
  763.     DeleteMsgPort(*ParentPort);
  764.     *ParentPort = NULL;
  765.     UnLock(homeDir);
  766.     Close(childOI);
  767.     FreePathList(pathList);
  768.  
  769.     return ERROR_NO_FREE_STORE;
  770.   }
  771.  
  772.   /* now pass the child the startup message */
  773.   PutMsg(&child->pr_MsgPort, (struct Message *) childMsg);
  774.  
  775.   return 0;
  776. } /* ExecCommand */
  777.  
  778.  
  779. STATIC BPTR
  780. CloneProcessIO(struct Process *Friend, LONG IoDirection)
  781. {
  782.   BPTR origFH;
  783.   BPTR lock;
  784.   BPTR fh = NULL;
  785.   LONG pos = -1;
  786.  
  787.   if (IoDirection == PPIPE_READ)
  788.     origFH = Friend->pr_CIS;
  789.   else
  790.     origFH = Friend->pr_COS;
  791.  
  792.   if (origFH)
  793.   {
  794.     pos = Seek(origFH, 0, OFFSET_CURRENT);
  795.     if (IoErr()) /* Handle V36/37 Seek() bug.  */
  796.       pos = -1;
  797.   }
  798.  
  799.   if ((lock = DupLockFromFH(origFH)) == NULL ||
  800.       (fh = OpenFromLock(lock)) == NULL)
  801.   {
  802.     if (lock)
  803.       UnLock(lock);
  804.     fh = Open("CONSOLE:", MODE_OLDFILE);
  805.   }
  806.   else if (fh && pos > 0)
  807.     Seek(fh, pos, OFFSET_BEGINNING);
  808.  
  809.   return fh;
  810. } /* CloneProcessIO */
  811.  
  812.  
  813. /*
  814.  *  NOTE: This routine should be called from within a Forbid();
  815.  *        but it is still unsafe due to the call to DupLock().
  816.  */
  817. STATIC BPTR
  818. ClonePathList(struct CommandLineInterface *Peer)
  819. {
  820.   BPTR pathList = NULL;
  821.   struct PathEntry *pathEntry, *pathET;
  822.  
  823.   if (Peer)
  824.   {
  825.     for (pathEntry = BADDR(Peer->cli_CommandDir),
  826.          pathET = (struct PathEntry *) &pathList;
  827.          pathEntry;
  828.          pathEntry = BADDR(pathEntry->pe_NextPathEntry))
  829.     {
  830.       struct PathEntry *newPE;
  831.  
  832.       if (newPE = AllocMem(sizeof(struct PathEntry), MEMF_PUBLIC))
  833.       {
  834.         newPE->pe_NextPathEntry = NULL;
  835.         newPE->pe_PathLock = DupLock(pathEntry->pe_PathLock);
  836.         pathET->pe_NextPathEntry = MKBADDR(newPE);
  837.         pathET = newPE;
  838.       }
  839.       else
  840.         break;
  841.     }
  842.   }
  843.  
  844.   return pathList;
  845. } /* ClonePathList */
  846.  
  847.  
  848. STATIC void
  849. FreePathList(BPTR PathList)
  850. {
  851.   struct PathEntry *pathEntry;
  852.  
  853.   while (pathEntry = BADDR(PathList))
  854.   {
  855.     PathList = pathEntry->pe_NextPathEntry;
  856.     UnLock(pathEntry->pe_PathLock);
  857.     FreeMem(pathEntry, sizeof(struct PathEntry));
  858.   }
  859. } /* FreePathList */
  860.